std_fork\alloc/
selfless_allocator.rs

1use core::alloc::Layout;
2use core::ptr::{self, NonNull};
3
4use stdx::alloc::AllocError;
5
6/// A global allocator interface that does not rely on `self`.
7///
8/// `SelflessAllocator` is suitable for stateless or global allocators
9/// where the allocation logic does not require access to instance state.
10///
11/// This trait is intended for use cases such as custom global allocators,
12/// embedded environments, or statically known allocators.
13///
14/// # Safety
15///
16/// Implementors must ensure that all memory returned:
17/// - is non-null,
18/// - properly aligned as per the `Layout`,
19/// - does not alias any existing live memory,
20/// - and is freed safely using `deallocate`.
21///
22/// Additionally, `grow`, `grow_zeroed`, and `shrink` must behave correctly
23/// even if called in rapid succession.
24pub unsafe trait SelflessAllocator {
25    /// Allocates a block of memory described by the given layout.
26    ///
27    /// # Errors
28    ///
29    /// Returns `Err(AllocError)` if:
30    /// - the system is out of memory,
31    /// - the alignment is invalid or unsupported,
32    /// - or the size exceeds allocator or platform limits.
33    fn allocate(layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
34
35    /// Allocates zero-initialized memory.
36    ///
37    /// This is equivalent to `allocate` followed by `write_bytes` with `0`.
38    ///
39    /// # Errors
40    ///
41    /// Returns `Err(AllocError)` under the same conditions as `allocate`,
42    /// or if zeroing the memory would cause undefined behavior.
43    fn allocate_zeroed(layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
44        let ptr = Self::allocate(layout)?;
45        unsafe { ptr.cast::<u8>().as_ptr().write_bytes(0, ptr.len()) }
46        Ok(ptr)
47    }
48
49    /// Deallocates a previously allocated block of memory.
50    ///
51    /// # Safety
52    ///
53    /// - `ptr` must have been returned by a previous call to `allocate` or `allocate_zeroed`.
54    /// - `layout` must match the original layout used in the allocation.
55    /// - Double-free or invalid layout can lead to undefined behavior.
56    unsafe fn deallocate(ptr: NonNull<u8>, layout: Layout);
57
58    /// Grows a memory block to a new layout.
59    ///
60    /// The contents up to the lesser of the old and new sizes are preserved.
61    /// The old memory is deallocated.
62    ///
63    /// # Errors
64    ///
65    /// Returns `Err(AllocError)` if:
66    /// - a new allocation fails,
67    /// - or copying or deallocation fails.
68    ///
69    /// # Safety
70    ///
71    /// - `ptr` must have been allocated by this allocator.
72    /// - `old_layout` must accurately reflect the original allocation.
73    /// - `new_layout.size()` must be greater than or equal to `old_layout.size()`.
74    unsafe fn grow(
75        ptr: NonNull<u8>,
76        old_layout: Layout,
77        new_layout: Layout,
78    ) -> Result<NonNull<[u8]>, AllocError> {
79        debug_assert!(
80            new_layout.size() >= old_layout.size(),
81            "`new_layout.size()` must be >= `old_layout.size()`"
82        );
83
84        let new_ptr = Self::allocate(new_layout)?;
85
86        unsafe {
87            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), old_layout.size());
88            Self::deallocate(ptr, old_layout);
89        }
90
91        Ok(new_ptr)
92    }
93
94    /// Grows and zero-initializes additional memory beyond the old size.
95    ///
96    /// The contents of the original allocation are preserved. The added
97    /// space between `old_layout.size()` and `new_layout.size()` is set to zero.
98    ///
99    /// # Errors
100    ///
101    /// Returns `Err(AllocError)` if:
102    /// - memory cannot be allocated,
103    /// - or copying/zeroing fails.
104    ///
105    /// # Safety
106    ///
107    /// Same as `grow`.
108    unsafe fn grow_zeroed(
109        ptr: NonNull<u8>,
110        old_layout: Layout,
111        new_layout: Layout,
112    ) -> Result<NonNull<[u8]>, AllocError> {
113        debug_assert!(
114            new_layout.size() >= old_layout.size(),
115            "`new_layout.size()` must be >= `old_layout.size()`"
116        );
117
118        let new_ptr = Self::allocate_zeroed(new_layout)?;
119
120        unsafe {
121            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), old_layout.size());
122            Self::deallocate(ptr, old_layout);
123        }
124
125        Ok(new_ptr)
126    }
127
128    /// Attempts to shrink a memory block to a smaller size.
129    ///
130    /// The contents up to the new layout size are preserved.
131    ///
132    /// # Errors
133    ///
134    /// Returns `Err(AllocError)` if:
135    /// - memory cannot be reallocated,
136    /// - or copying fails.
137    ///
138    /// # Safety
139    ///
140    /// - `ptr` must have been allocated by this allocator.
141    /// - `old_layout` must match the original allocation.
142    /// - `new_layout.size()` must be less than or equal to `old_layout.size()`.
143    unsafe fn shrink(
144        ptr: NonNull<u8>,
145        old_layout: Layout,
146        new_layout: Layout,
147    ) -> Result<NonNull<[u8]>, AllocError> {
148        debug_assert!(
149            new_layout.size() <= old_layout.size(),
150            "`new_layout.size()` must be <= `old_layout.size()`"
151        );
152
153        let new_ptr = Self::allocate(new_layout)?;
154
155        unsafe {
156            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), new_layout.size());
157            Self::deallocate(ptr, old_layout);
158        }
159
160        Ok(new_ptr)
161    }
162}